package ckw.manager;

import java.net.BindException;
import java.util.List;

import javax.inject.Inject;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.util.Assert;

import ckw.communication.CommunicationService;
import ckw.database.DatabaseService;
import ckw.database.model.Election;
import ckw.database.model.ElectionChanges;
import ckw.database.model.ElectionNode;
import ckw.database.model.ElectionNonResidentVoter;
import ckw.database.model.ElectionResidentVoter;
import ckw.database.model.Member;
import ckw.database.model.Node;
import ckw.database.model.Voter;
import ckw.election.ElectionService;
import ckw.gui.MainWindow;
import ckw.node.NodesService;
import core.cards.election.model.ElectionResultBackend;
import core.database.NodeType;
import core.exceptions.CommunicationInitiationException;
import core.exceptions.ObjectDeleteException;
import core.exceptions.ObjectUpdateException;
import core.exceptions.PortInUseException;
import core.exceptions.WrongActionException;
import core.utils.Converter;
import core.utils.LocatorUriChecker;

@Controller
public class ManagerControllerImpl implements ManagerController {

	private static final Logger L = LoggerFactory.getLogger(ManagerControllerImpl.class);

	private MainWindow mainWindow;
	private DatabaseService databaseService;
	private CommunicationService communicationService;
	private NodesService nodesService;
	private ElectionService electionService;

	@Inject
	public ManagerControllerImpl(MainWindow mainWindow, DatabaseService databaseService, CommunicationService communicationService, NodesService nodesService, ElectionService electionService) {
		L.debug("Tworzę ManagerController");

		Assert.notNull(mainWindow);
		Assert.notNull(databaseService);
		Assert.notNull(communicationService);
		Assert.notNull(nodesService);
		Assert.notNull(electionService);

		this.mainWindow = mainWindow;
		this.databaseService = databaseService;
		this.communicationService = communicationService;
		this.nodesService = nodesService;
		this.electionService = electionService;

		L.debug("Utworzono ManagerController");
	}

	@Override
	public void setUp() throws CommunicationInitiationException, PortInUseException {
		L.debug("setUp");

		Node node = nodesService.getMainNode();

		if (node == null) {
			node = mainWindow.askForMainNodeData();
			if (!LocatorUriChecker.checkIfLocatorUriIsOpen(node.getIp(), node.getPort())) {
				L.error("Wrong server locatorURI.");
				throw new CommunicationInitiationException("Wrong server locatorURI.");
			}
			nodesService.addNode(node);
		}
		try {
			communicationService.setUp();
		} catch (BindException e) {
			if (!LocatorUriChecker.checkIfLocatorUriIsOpen(node.getIp(), node.getPort())) {
				try {
					databaseService.deleteNode(node);
				} catch (ObjectDeleteException e1) {
					L.error(e1.getMessage());
				}
				L.error("Wrong server locatorURI.");
				throw new CommunicationInitiationException("Wrong server locatorURI.");
			} else {
				L.error("The port is already in use.");
				throw new PortInUseException("The port is already in use.");
			}
		}
	}

	@Override
	public void shutDown() {
		L.debug("shutDown");

		communicationService.shutDown();
	}

	@Override
	public Node saveNode(Node node) {
		L.debug("Store node");

		return nodesService.addNode(node);
	}

	@Override
	public void deleteNode(Node node) throws ObjectDeleteException {
		nodesService.deleteNode(node);
	}

	@Override
	public Node updateNode(Node node) {
		return nodesService.updateNode(node);
	}

	@Override
	public void sendConfigurations() {
		communicationService.sendConfigurations();
	}

	@Override
	public Node getBaseNode() {
		return nodesService.getMainNode().getParent();
	}

	@Override
	public List<Node> getAllRkwNodes() {
		return databaseService.getAllNodesByType(NodeType.RKW);
	}

	@Override
	public Boolean isMemberIdentificationFieldTaken(String identificationField) {
		return databaseService.isMemberIdentificationFieldTaken(identificationField);
	}

	@Override
	public Boolean isNodeLocatorUriTaken(String ip, String port) {
		return databaseService.isNodeLocatorUriTaken(ip, port);
	}

	@Override
	public List<Member> getAllMembers() {
		return databaseService.getAllMembers();
	}

	@Override
	public Member storeMember(Member member) throws ObjectUpdateException {
		return databaseService.storeMember(member);
	}

	@Override
	public void deleteMember(Member member) throws ObjectDeleteException {
		databaseService.deleteMember(member);
	}

	@Override
	public Node getNode(Node node) {
		return databaseService.getNode(node.getId());
	}

	@Override
	public List<Election> getAllElections() {
		return databaseService.getAllElections();
	}

	@Override
	public Boolean isVoterIdentificationFieldTaken(String identificationField) {
		return databaseService.isVoterIdentificationFieldTaken(identificationField);
	}

	@Override
	public Voter storeVoter(Voter voter) throws ObjectUpdateException {
		return databaseService.storeVoter(voter);
	}

	@Override
	public void deleteVoter(Voter voter) throws ObjectDeleteException {
		databaseService.deleteVoter(voter);
	}

	@Override
	public List<Voter> getAllVoters() {
		return databaseService.getAllVoters();
	}

	@Override
	public void addMember(ElectionNode electionNode, Member member) {
		electionService.addMember(electionNode, member);
	}

	@Override
	public void removeMember(ElectionNode electionNode, Member member) {
		electionService.removeMember(electionNode, member);
	}

	@Override
	public List<Voter> findVotersLike(String text) {
		return databaseService.findVotersLike(text);
	}

	@Override
	public void addResidentVoter(Election election, Voter voter) {
		electionService.addResidentVoter(election, voter);
	}

	@Override
	public void addNonResidentVoter(Election election, Voter voter) {
		electionService.addNonResidentVoter(election, voter);
	}

	@Override
	public void removeResidentVoter(ElectionResidentVoter electionResidentVoter) {
		electionService.removeResidentVoter(electionResidentVoter);
	}

	@Override
	public void removeNonResidentVoter(ElectionNonResidentVoter electionNonResidentVoter) {
		electionService.removeNonResidentVoter(electionNonResidentVoter);
	}

	@Override
	public Voter getVoterByIdentificationField(String identificationField) {
		return databaseService.getVoterByIdentificationField(identificationField);
	}

	@Override
	public Election saveElection(Election election) {
		return electionService.saveElection(election);
	}

	@Override
	public Election updateElection(Election election) throws WrongActionException {
		return electionService.updateElection(election);
	}

	@Override
	public void deleteElection(Election election) throws ObjectDeleteException {
		electionService.deleteElection(election);
	}

	@Override
	public Election getElection(Long id) {
		return databaseService.getElection(id);
	}

	@Override
	public List<ElectionChanges> getAllElectionChanges() {
		return databaseService.getAllElectionChages();
	}

	@Override
	public ElectionChanges getElectionChanges(String nodeUri, Long electionIdentificationField) {
		return databaseService.getElectionChanges(nodeUri, electionIdentificationField);
	}

	@Override
	public void sendElectionConfiguration(Election election) {
		communicationService.sendElectionConfiguration(election);
	}

	@Override
	public ElectionResultBackend getElectionResult(Election selectedElection) {
		Election election = databaseService.getElection(selectedElection.getId());
		return election.getResults() != null ? (ElectionResultBackend) Converter.xmlToObject(election.getResults()) : null;
	}

	@Override
	public Node getMainNode() {
		return databaseService.getMainNode();
	}
}